package com.coderman.common.starter.excelport.imports.service;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.coderman.common.starter.excelport.export.bean.ConverterFieldBean;
import com.coderman.common.starter.excelport.export.service.impl.CommonExportServiceImpl;
import com.coderman.common.starter.excelport.imports.beans.ImportFieldBean;
import com.coderman.common.starter.excelport.imports.beans.ImportTitleBean;
import com.coderman.common.starter.excelport.imports.beans.PrimaryKeyBean;
import com.coderman.common.starter.excelport.imports.config.ImportBeanConfig;
import com.coderman.common.starter.excelport.imports.service.common.ImportConvertService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Description:
 * date: 2020/9/15 9:32 上午
 *
 * @author fanchunshuai
 * @version 1.0.0
 * @since JDK 1.8
 */
@Service(value = "globalExcelListener")
public class GlobalExcelListener<T> extends AnalysisEventListener<T> {
    private Logger logger = LoggerFactory.getLogger(CommonExportServiceImpl.class);

    private final CopyOnWriteArrayList<T> rows = new CopyOnWriteArrayList<>();

    private ImportBeanConfig importBeanConfig;

    private ImportTitleBean importTitleBean;

    private ConcurrentHashMap<String, ImportFieldBean> importFieldBeanMap;

    @Autowired
    private ImportConvertService importConvertService;


    public ConcurrentHashMap<String, ImportFieldBean> getImportFieldBeanMap() {
        return importFieldBeanMap;
    }

    public void setImportFieldBeanMap(ConcurrentHashMap<String, ImportFieldBean> importFieldBeanMap) {
        this.importFieldBeanMap = importFieldBeanMap;
    }

    public ImportBeanConfig getImportBeanConfig() {
        return importBeanConfig;
    }

    public void setImportBeanConfig(ImportBeanConfig importBeanConfig) {
        this.importBeanConfig = importBeanConfig;
    }


    public ImportTitleBean getImportTitleBean() {
        return importTitleBean;
    }

    public void setImportTitleBean(ImportTitleBean importTitleBean) {
        this.importTitleBean = importTitleBean;
    }

    @Override
    public void invoke(T data, AnalysisContext context)  {

        //使用批量导入
        if(importBeanConfig.isUseBatch()){
            if(importBeanConfig.getLimitCount() <= 1){
                //单条导入
                //1. 数据源替换
                exeDataConvert(data);
                //2. 1拆N-->3. 聚合
                ConcurrentHashMap<String, Object> targetListMap = convert2EntityObject(data);
                //4.事务单条插入
                exeInsert(targetListMap);
            }
            if(importBeanConfig.getLimitCount() > 1 && importBeanConfig.getLimitCount() < rows.size()){
                //并行导入
                rows.parallelStream().forEach(d->{
                    //单条导入
                    //1. 数据源替换
                    exeDataConvert(d);
                    //2. 1拆N-->3. 聚合
                    ConcurrentHashMap<String, Object> targetListMap = convert2EntityObject(d);
                    //4. 事务单条插入
                    exeInsert(targetListMap);
                });
                rows.clear();
            }else {
                rows.add(data);
            }
        }else {
            //单条导入
            //1. 数据源替换
            exeDataConvert(data);
            //2. 1拆N-->3. 聚合
            ConcurrentHashMap<String, Object> targetListMap = convert2EntityObject(data);
            //4.事务单条插入
            exeInsert(targetListMap);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        //1.数据源替换
        logger.info("read {} rows %n", rows.size());
    }

    /**
     * 进行数据源替换，在转换成entity之前数据源完全替换完成
     * @param data
     */
    private void exeDataConvert(T data){
        //1. 数据源替换
        try {
            importConvertService.exeCustomConverter(data,importBeanConfig,importFieldBeanMap);
        } catch (Exception e) {
            logger.error("exe common convert exception",e);
        }

        List<ObjectConverter> objectConverterList = importBeanConfig.getObjectConverter();
        if(CollectionUtils.isEmpty(objectConverterList)){
            return;
        }
        try {
            for (int i = 0; i < objectConverterList.size(); i++) {
                ConverterFieldBean converterFieldBean = importBeanConfig.getConverterFieldBeanList().get(i);
                objectConverterList.get(i).convertBatchFieldData(data, converterFieldBean);
            }
        } catch (Exception e) {
            logger.error("exe custom convert exception",e);
        }
    }

    /**
     * 将数据模型转换为各个主，子表的entity
     * @param data
     * @return
     */
    private ConcurrentHashMap<String,Object> convert2EntityObject(T data){
        ConcurrentHashMap<String, Object> targetListMap = new ConcurrentHashMap<>();
        //2. 1拆N
        for (Map.Entry<String, Class<?>> entry : importBeanConfig.getEntityTableMap().entrySet()){
            Object target = importBeanConfig.getDefaultColumnEntityMap().get(entry.getKey());
            if(target == null){
                try {
                    target = entry.getValue().newInstance();
                } catch (InstantiationException e) {
                    logger.error("newInstance error,table="+entry.getKey(),e);
                } catch (IllegalAccessException e) {
                    logger.error("newInstance error,table="+entry.getKey(),e);
                }
            }
            //todo 改成cglib
            BeanUtils.copyProperties(data, target);
            //3. 聚合
            targetListMap.put(entry.getKey(),target);
        }
        return targetListMap;
    }

    /**
     * 执行插入
     * @param targetListMap
     */
    //@Transactional(rollbackFor = Exception.class)
    public void exeInsert(final ConcurrentHashMap<String,Object> targetListMap) {
        //4. 编排插入
        // todo 增加事务支持
        Map<String, PrimaryKeyBean> primaryKeyBeanMap = importTitleBean.getPrimaryKeyBeanMap();
        for(Map.Entry<String,PrimaryKeyBean> entry : primaryKeyBeanMap.entrySet()){
            Object mainObject = targetListMap.get(entry.getKey());
            InsertTaskService mainTaskService = importBeanConfig.getInsertTaskServiceBeanMap().get(entry.getKey());
            //插入主表
            mainTaskService.insert(mainObject);
            Field field = ReflectionUtils.findField(mainObject.getClass(),entry.getValue().getMainTableIdField());
            Object primaryKey = ReflectionUtils.getField(field,mainObject);
            Map<String, String> childTableMap = entry.getValue().getChildTableMap();
            if(childTableMap != null && !childTableMap.isEmpty()){
                for (Map.Entry<String, String> childEntry : childTableMap.entrySet()){
                    Object childObject = targetListMap.get(childEntry.getKey());
                    Field childField = ReflectionUtils.findField(childObject.getClass(),childEntry.getValue());
                    //将主表的ID关联到子表的属性上面
                    ReflectionUtils.setField(childField,childObject,primaryKey);
                    targetListMap.put(childEntry.getKey(),childObject);
                }
            }
        }

        CopyOnWriteArraySet<String> childTableSet = new CopyOnWriteArraySet<>();
        childTableSet.addAll(Arrays.asList(importTitleBean.getChildArr()));

        for(Map.Entry<String,PrimaryKeyBean> entry : primaryKeyBeanMap.entrySet()){
            Map<String, String> childTableMap = entry.getValue().getChildTableMap();
            if(childTableMap != null && !childTableMap.isEmpty()){
                for (Map.Entry<String, String> childEntry : childTableMap.entrySet()){
                    if(!childTableSet.contains(childEntry.getKey())){
                        continue;
                    }
                    InsertTaskService mainTaskService =
                            importBeanConfig.getInsertTaskServiceBeanMap().get(childEntry.getKey());
                    Object childObject = targetListMap.get(childEntry.getKey());
                    //插入子表
                    mainTaskService.insert(childObject);
                    childTableSet.remove(childEntry.getKey());
                }
            }
        }
    }
}
